/*************************************************
 Copyright © DusunIoT 2005-2023. All rights reserved.
 File name: common_dbg_log.c
 Author: yaoyuxi
 Version: V1.0
 Date: Tuesday, July 11, 2023 
 Description: 调试打印日志统一接口定义
 Others: none
 History: Created July 11, 2023  by:yaoyuxi
*************************************************/

#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <syslog.h>
#include <stdarg.h>
#include <stdlib.h>
#include <libgen.h>
#include <assert.h>

#include "common_types.h"
#include "common_dbg_log.h"

#define LOG_MAX_MOULDE_MASK_COUNT              100

typedef struct 
{
    RB_UINT32 module_id;
    RB_UINT32 module_mask;
}log_modulemask_map_t;

static RB_UINT32 g_iDbgLogLevel = DBG_PRIT;    /* 串口日志等级，控制串口日志输出*/
static log_modulemask_map_t g_iDbgLogMoudleId[LOG_MAX_MOULDE_MASK_COUNT]; /* 串口日志模块ID，控制串口日志输出*/
static RB_UINT32 log_modulemask_map_count = 0;
static RB_UINT32 g_iDbgLogMoudleMask = DBG_LOG_DEFAULT_MODULE_MASK;    /* 串口日志模块掩码，控制串口日志输出*/

static RB_VOID dbg_log_set_module(RB_UINT32 mask, RB_UINT32 moduleid);
static RB_VOID dbg_log_clear_module(RB_UINT32 mask, RB_UINT32 moduleid);

/**@brief     判断对应等级和对应子模块是否满足设置的输出条件
* @param[in]  uiLevel:日志等级
* @param[in]  uiModuleId:日志模块ID
* @param[in]  uiModuleMask:日志模块掩码
* @param[out] 无
* @return     是否支持日志打印
*/
RB_BOOL dbg_log_is_open(RB_UINT32 uiLevel, RB_UINT32 uiModuleId, RB_UINT32 uiModuleMask)
{
    RB_BOOL bRet = RB_TRUE;
    
    if (uiLevel <= DBG_ERR) 
    {
        bRet = RB_TRUE;
    }
    else if (uiLevel <= g_iDbgLogLevel)
    {
        if (RB_FALSE != (g_iDbgLogMoudleMask & uiModuleMask)) 
        {
            RB_UINT32 dbgLogMoudleId = dbg_log_get_module(uiModuleMask);
            if (RB_FALSE != (dbgLogMoudleId & uiModuleId))
            {
                bRet = RB_TRUE;
            }
            else 
            {
                bRet = RB_FALSE;
            }
        }
        else
        {
            bRet = RB_FALSE;
        }
    }
    else
    {
        bRet = RB_FALSE;
    }

    return bRet;
}

/**@brief     打开某个或某几个模块的某个等级的串口打印信息
* @param[in]  uiModuleId:日志模块ID
* @param[in]  uiModuleMask:日志模块掩码
* @param[out] 无
* @return     无
*/
RB_VOID dbg_log_set_open(RB_UINT32 uiLevel, RB_UINT32 uiModuleId, RB_UINT32 uiModuleMask)
{
    RB_BOOL bRet = RB_TRUE;
    
    if (uiLevel <= DBG_ERR) 
    {
        g_iDbgLogLevel = DBG_ERR;
    }
    else
    {
        g_iDbgLogLevel = uiLevel;
    }

    g_iDbgLogMoudleMask |= uiModuleMask;
    dbg_log_set_module(uiModuleMask, uiModuleId);
    return ;
}

/**@brief     关闭某个或某几个模块的串口打印信息
* @param[in]  uiModuleId:日志模块ID
* @param[in]  uiModuleMask:日志模块掩码
* @param[out] 无
* @return     无
*/
RB_VOID dbg_log_set_close(RB_UINT32 uiModuleId, RB_UINT32 uiModuleMask)
{
    dbg_log_clear_module(uiModuleMask, uiModuleId); 
    return;
}

/**@brief     获取日志时间戳字符串
* @param[in]  无
* @param[out] 无
* @return    日志字符串地址指针
*/
RB_INT8 *dbg_log_get_time_str()
{
    time_t stTimeval;
    struct timeval stTimevalus;
    struct tm* stCal;
    static RB_INT8 acBuf[64];

    memset(&stTimeval, 0, sizeof(stTimeval));
    memset(acBuf, 0, sizeof(acBuf));
    
    time(&stTimeval);
    stCal = localtime(&stTimeval);

    gettimeofday(&stTimevalus, NULL);

    snprintf(acBuf, sizeof(acBuf),
         "%04d-%02d-%02dT%02d:%02d:%02d.%03u:",
          stCal->tm_year + 1900,
          stCal->tm_mon + 1,
          stCal->tm_mday,
          stCal->tm_hour,
          stCal->tm_min,
          stCal->tm_sec,
          (unsigned)(stTimevalus.tv_usec / 1000));
          
    return acBuf;
}

/**@brief     获取日志时间戳字符串
* @param[in]  iBufSize:预存放时间戳字符串buf大小
* @param[out] acBuf：时间戳字符串存放地址
* @return    无
*/
static RB_INT32 dbg_log_get_timestamp(RB_INT8 *acBuf, RB_INT32 iBufSize)
{
    time_t stTimeval;
    struct timeval stTimevalus;
    struct tm* stCal;
    RB_INT32 iTempLen = 0;

    memset(&stTimeval, 0, sizeof(stTimeval));
    time(&stTimeval);
    stCal = localtime(&stTimeval);

    gettimeofday(&stTimevalus, NULL);

    iTempLen = snprintf(acBuf, iBufSize,
         "%04d-%02d-%02dT%02d:%02d:%02d.%03u:",
          stCal->tm_year + 1900,
          stCal->tm_mon + 1,
          stCal->tm_mday,
          stCal->tm_hour,
          stCal->tm_min,
          stCal->tm_sec,
          (unsigned)(stTimevalus.tv_usec / 1000));
          
    if (iTempLen < iBufSize) {
        return iTempLen;
    }
    return iBufSize - 1;
}

/**@brief     获取日志输出流文件(stdout or stderr)
* @param[in]  uiLevel:日志等级
* @return    stdout or stderr
*/
static RB_FILE *dbg_log_get_console_stream(RB_UINT32 uiLevel)
{
    if (uiLevel <= DBG_WARN)
    {
        return stderr;
    }
    return stdout;
}


/**@brief     输出串口打印日志信息
* @param[in]  pDbgFile:文件名
* @param[in]  pDbgLine:文件中代码行数
* @param[in]  pDbgFunc:函数名
* @param[in]  uiLevel:串口日志优先级
* @param[in]  pFmt:输出串口打印日志格式
* @param[out] 无
* @return    无
*/
RB_VOID dbg_log_base_output(RB_INT8 *pDbgFile, const RB_INT32 pDbgLine, const RB_INT8 *pDbgFunc, const RB_UINT32 uiLevel, const RB_INT8 *pFmt, ...)
{
    RB_INT32 iTempLen = 0;
    RB_INT8 acFmtBuf[LOG_MAX_FMT_STR_SIZE];
    va_list stArgs;
    RB_INT8 *pDbgFileName = RB_NULL;
    int iFmtlen = strlen(pFmt);

    memset(acFmtBuf, 0, sizeof(acFmtBuf));
    
    va_start(stArgs, pFmt);
    iTempLen = dbg_log_get_timestamp(acFmtBuf, sizeof(acFmtBuf));
    
    if ((RB_NULL == pDbgFile) || (RB_NULL == pDbgFunc))
    {
        printf(DBG_LOG_FMT_BOLD(DBG_LOG_RED) "%s:%s input pDbgFile pDbgFunc err!\n", acFmtBuf, DBG_FUNC);
        va_end(stArgs);
        return;
    }

    pDbgFileName = basename(pDbgFile);
    if (RB_NULL != pDbgFileName)
    {
        iTempLen += snprintf(acFmtBuf + iTempLen, sizeof(acFmtBuf) - iTempLen, "File:%s, ", pDbgFileName);
        if (iTempLen >= sizeof(acFmtBuf))
        {
            printf(DBG_LOG_FMT_BOLD(DBG_LOG_RED) "%s: acFmtBuf is too short err!\n",  DBG_FUNC);
            va_end(stArgs);
            return;
        }
    }
    
    iTempLen += snprintf(acFmtBuf + iTempLen, sizeof(acFmtBuf) - iTempLen, "Line:%d, ", pDbgLine);
    if (iTempLen >= sizeof(acFmtBuf))
    {
        printf(DBG_LOG_FMT_BOLD(DBG_LOG_RED) "%s: acFmtBuf is too short err!\n", DBG_FUNC);
        va_end(stArgs);
        return;
    }
    
    iTempLen += snprintf(acFmtBuf + iTempLen, sizeof(acFmtBuf) - iTempLen, "Func:%s, ", pDbgFunc);
    if (iTempLen >= sizeof(acFmtBuf))
    {
        printf(DBG_LOG_FMT_BOLD(DBG_LOG_RED) "%s: acFmtBuf is too short err!\n", DBG_FUNC);
        va_end(stArgs);
        return;
    }
    /* complete format string */
    if (pFmt[iFmtlen-1] != '\n' && pFmt[iFmtlen-1] != '\r') 
    {
        iTempLen += snprintf(acFmtBuf + iTempLen, sizeof(acFmtBuf) - iTempLen, "%s\n", pFmt);
    } 
    else
    {
        iTempLen += snprintf(acFmtBuf + iTempLen, sizeof(acFmtBuf) - iTempLen, "%s", pFmt);
    }

    if (iTempLen >= sizeof(acFmtBuf)) 
    {
#ifdef WITH_LOG4C
        const log4c_category_t* a_category = log4c_category_get(HSLOG_CATEGORY_NAME);
        /* if fmt_buf is too small, just print the message */
        log4c_category_vlog(a_category, uiLevel, pFmt, stArgs);
#endif
        vfprintf(dbg_log_get_console_stream(uiLevel), pFmt, stArgs);
    } 
    else 
    {
#ifdef WITH_LOG4C
        const log4c_category_t* a_category = log4c_category_get(HSLOG_CATEGORY_NAME);
        log4c_category_vlog(a_category, uiLevel, acFmtBuf, stArgs);
#endif
        vfprintf(dbg_log_get_console_stream(uiLevel), acFmtBuf, stArgs);
    }
    va_end(stArgs);
    
    return;
}

/**
 * @brief 获取全局日志输出等级
 * 
 * @return RB_UINT32 
 */
RB_UINT32 dbg_log_get_current_level()
{
    return g_iDbgLogLevel;
}

/**
 * @brief 获取全局日志相应功能mask输出模块设置
 * 
 * @return RB_UINT32 
 */
RB_UINT32 dbg_log_get_module(RB_UINT32 maskid)
{
    RB_UINT32 dbgLogMoudleId = DBG_LOG_DEFAULT_MODULE_ID;
    if (log_modulemask_map_count == 0)
    {
        return dbgLogMoudleId;
    }

    /* Shoule not occur */
    if (LOG_MAX_MOULDE_MASK_COUNT < log_modulemask_map_count)
    {
        return dbgLogMoudleId;
    }

    for (RB_UINT32 i=0; i<log_modulemask_map_count; i++)
    {
        if (g_iDbgLogMoudleId[i].module_mask == maskid)
        {
            dbgLogMoudleId = g_iDbgLogMoudleId[i].module_id;
            break;
        }
    }

    return dbgLogMoudleId;
}

/**
 * @brief 获取全局日志相应功能mask输出模块设置
 * 
 * @return RB_UINT32 
 */
RB_VOID dbg_log_set_module(RB_UINT32 mask, RB_UINT32 moduleid)
{
    RB_BOOL hasfind = RB_FALSE;
    /* Shoule not occur */
    assert (LOG_MAX_MOULDE_MASK_COUNT > log_modulemask_map_count);

    for (RB_UINT32 i=0; i<log_modulemask_map_count; i++)
    {
        /* 设置全部模块 */
        if (mask == DBG_LOG_DEFAULT_MODULE_MASK)
        {
            g_iDbgLogMoudleId[i].module_id |= moduleid;
            continue;
        }
        else if (g_iDbgLogMoudleId[i].module_mask == mask)
        {
            g_iDbgLogMoudleId[i].module_id |= moduleid;
            hasfind = RB_TRUE;
            break;
        }
    }

    if ( (mask != DBG_LOG_DEFAULT_MODULE_MASK)
        && (!hasfind) )
    {
        if (LOG_MAX_MOULDE_MASK_COUNT > log_modulemask_map_count )
        {
            g_iDbgLogMoudleId[log_modulemask_map_count].module_mask = mask;
            g_iDbgLogMoudleId[log_modulemask_map_count].module_id = moduleid;
            log_modulemask_map_count++;
        }
    }

    return;
}

/**
 * @brief 获取全局日志相应功能mask输出模块设置
 * 
 * @return RB_UINT32 
 */
RB_VOID dbg_log_clear_module(RB_UINT32 mask, RB_UINT32 moduleid)
{
    RB_BOOL hasfind = RB_FALSE;
    /* Shoule not occur */
    assert (LOG_MAX_MOULDE_MASK_COUNT > log_modulemask_map_count);

    for (RB_UINT32 i=0; i<log_modulemask_map_count; i++)
    {
        /* 设置全部模块 */
        if (mask == DBG_LOG_DEFAULT_MODULE_MASK)
        {
            g_iDbgLogMoudleId[i].module_id &= ~moduleid;
            continue;
        }
        else if (g_iDbgLogMoudleId[i].module_mask == mask)
        {
            g_iDbgLogMoudleId[i].module_id &= ~moduleid;
            hasfind = RB_TRUE;
            break;
        }
    }

    if ( (mask != DBG_LOG_DEFAULT_MODULE_MASK)
        && (!hasfind) )
    {
        if (LOG_MAX_MOULDE_MASK_COUNT > log_modulemask_map_count )
        {
            g_iDbgLogMoudleId[log_modulemask_map_count].module_mask = mask;
            g_iDbgLogMoudleId[log_modulemask_map_count].module_id = 0;
            log_modulemask_map_count++;
        }
    }

    return;
}
/**
 * @brief 获取全局日志输出掩码配置
 * 
 * @return RB_UINT32 
 */
RB_UINT32 dbg_log_get_current_mask()
{
    return g_iDbgLogMoudleMask;
}